package com.shiliuzi.www.util;

import com.shiliuzi.www.adapter.LimitAdapter;
import com.shiliuzi.www.common.Code;
import com.shiliuzi.www.exception.GlobalException;
import com.shiliuzi.www.pojo.bo.LimitBo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

@Slf4j
@Component
public class LimitUtil<T extends LimitAdapter> {

    public static Integer requestVersion = 1;

    public static final ThreadLocal<LimitBo> threadLocal = new ThreadLocal<>();

    /**
     * 自维护一个limitBo对象
     */
    private static LimitBo upholdTarget = new LimitBo();

    /**
     * 获取一个限制器
     * @return
     */
    public LimitBo obtainLimit() {
        if (threadLocal.get() == null) upholdLimit(upholdTarget);
        return threadLocal.get();
    }

    /**
     * 维护一个自定义限制器
     * @param limitBo
     */
    public void upholdLimit(LimitBo limitBo) {
        upholdTarget = limitBo;
        threadLocal.set(upholdTarget);
    }

    /**
     * 核心适配器
     * @param adapter
     * @return
     */
    @Transactional
    public LimitBo limitAdapter(T adapter, Integer pageMax) {
        if(obtainLimit().getType()!=adapter.getType()) {
            setLimitBo(adapter.getType()
                    ,adapter.getVersion() - 1
                    ,adapter.getPageSize());
            return upDownLimit(adapter.getChoose(),pageMax);
        } else if(obtainLimit().getVersion()-adapter.getVersion()>1 || obtainLimit().getVersion()-adapter.getVersion()<-1) {
            setLimitBo(adapter.getType()
                    , adapter.getVersion()-1
                    ,adapter.getPageSize());
            return upholdTarget;
        }
        return null;
    }

    /**
     * 自定义形式
     * @param type，查询类型转变
     * @param genericVersion
     * @param genericPage
     */
    public void setLimitBo(Integer type, Integer genericVersion, Integer genericPage) {
        LimitBo limitBo = obtainLimit();
        limitBo.setType(typeExchange(type));
        limitBo.setVersion(genericVersion);
        limitBo.setPageSize(genericPage);
        upholdLimit(limitBo);
    }

    /**
     * 查询转换类型后，进行参数重置
     * @param type
     * @return
     */
    public Integer typeExchange(Integer type) {
        LimitBo limitBo = obtainLimit();
        if (!type.equals(limitBo.getType())) clearLimit();
        return type;
    }

    /**
     * 参数重置
     * 既是类型转换时参数的重置，也是url不一致的重置
     */
    @Transactional
    public void clearLimit() {
        try {
            LimitBo limitBo = obtainLimit();
            Class className = limitBo.getClass();
            Field[] fields = className.getDeclaredFields();
            for(Field field : fields) {
                if (Modifier.isFinal(field.getModifiers())) continue;

                String fieldName = field.getName();
                String suffixField = fieldName.substring(0,1).toUpperCase() + fieldName.substring(1);

                Method setMethod = className.getMethod("set" + suffixField, field.getType());
                Method getMethod = className.getMethod("get" + suffixField + "Default");

                setMethod.invoke(limitBo,getMethod.invoke(limitBo));
            }
            upholdLimit(limitBo);
            requestVersion = 1;
        } catch (Exception e) {
            throw new GlobalException(Code.SYS_ERR, "系统内部出现错误！限制器重置有误！");
        }
    }

    /**
     * 上下页设置，以version作为调配页数
     * @param upDown--对应choose
     * @return
     */
    public LimitBo upDownLimit(Integer upDown, @Nullable Integer pageMax) {
        LimitBo limitBo = obtainLimit();
        Integer version = limitBo.getVersion();

        //非第一次访问
        if (!requestVersion.equals(1))
            if(upDown.equals(1) && pageMax >= version) {
                limitBo.setVersion(version + 1);
                limitBo.setLimitSize((version + 1) * limitBo.getPageSize());
            } else if (upDown.equals(0) && version >=1 ) {
                limitBo.setVersion(version - 1);
                limitBo.setLimitSize((version - 1) * limitBo.getPageSize());
            }
            else requestVersion++;

        return limitBo;
    }
}
